/*
 * @(#)System.c	1.106 06/10/10
 * 
 * Copyright  1990-2008 Sun Microsystems, Inc. All Rights Reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
 * 
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License version
 * 2 only, as published by the Free Software Foundation. 
 * 
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * General Public License version 2 for more details (a copy is
 * included at /legal/license.txt). 
 * 
 * You should have received a copy of the GNU General Public License
 * version 2 along with this work; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA 
 * 
 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
 * Clara, CA 95054 or visit www.sun.com if you need additional
 * information or have any questions. 
 */

#include "jni.h"
#include "jni_util.h"
#include "jvm.h"
#include "javavm/include/porting/java_props.h"
#include "java_lang_System.h"
#include "javavm/include/clib.h"
#include "generated/javavm/include/build_defs.h"

#ifdef __cplusplus
#define this XthisX
#endif

#ifdef CVM_JAVALANGSYSTEM_REGISTER_NATIVES

#define OBJ "Ljava/lang/Object;"

/* Only register the performance-critical methods */

static const JNINativeMethod methods[] = {
    {"currentTimeMillis", "()J",              (void *)&JVM_CurrentTimeMillis},
    {"arraycopy",     "(" OBJ "I" OBJ "II)V", (void *)&JVM_ArrayCopy},
};

#undef OBJ

JNIEXPORT void JNICALL
Java_java_lang_System_registerNatives(JNIEnv *env, jclass cls)
{
    (*env)->RegisterNatives(env, cls, methods, 2);
}

#endif

JNIEXPORT jint JNICALL
Java_java_lang_System_identityHashCode(JNIEnv *env, jobject this, jobject x)
{
    return JVM_IHashCode(env, x);
}

/*
 * Replace macros with functions to save code space, ala javai_md.c.
 */
#define PUTPROP(props, prop, value) \
    if (!CVMputProp(env, putID, props, prop, value)) return NULL;

#define PUTPROP_ForPlatformCString(props, prop, value) \
    if (!CVMputPropForPlatformCString(env, putID, props, prop, value)) return NULL;


JNIEXPORT jobject JNICALL
Java_java_lang_System_initProperties(JNIEnv *env, jclass cla, jobject props)
{
    java_props_t sprops;
    jobject result;

    jmethodID putID = (*env)->GetMethodID(env,
					  (*env)->GetObjectClass(env, props),
					  "setProperty",
            "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/Object;");

    if (putID == NULL) return NULL;

    memset(&sprops, 0, sizeof sprops);
    if (!CVMgetJavaProperties(&sprops)) {
	JNU_ThrowOutOfMemoryError(env, 0);
	return NULL;
    }

    /* CVM_PROP_*s are set in build_defs.h generated by defs.mk */
    PUTPROP(props, "java.specification.version",CVM_PROP_JAVA_SPEC_VERSION);
    PUTPROP(props, "java.specification.name",	CVM_PROP_JAVA_SPEC_NAME);
    PUTPROP(props, "java.specification.vendor",	CVM_PROP_JAVA_SPEC_VENDOR);
    PUTPROP(props, "java.version",		CVM_PROP_JAVA_VERSION);
    PUTPROP(props, "java.vendor",		CVM_PROP_JAVA_VENDOR);
    PUTPROP(props, "java.vendor.url",		CVM_PROP_JAVA_VENDOR_URL); 
    /* Note: java.vendor.url.bug is not required by the spec */
    /* PUTPROP(props, "java.vendor.url.bug",	CVM_PROP_JAVA_VENDOR_URL_BUG); */
    PUTPROP(props, "java.class.version",	CVM_PROP_JAVA_CLASS_VERSION);
    PUTPROP(props, "sun.misc.product",          CVM_PROP_SUN_MISC_PRODUCT);

#include "generated/javavm/runtime/system_properties.c"

    /* os properties */
    PUTPROP(props, "os.name", sprops.os_name);
    PUTPROP(props, "os.version", sprops.os_version);
    PUTPROP(props, "os.arch", sprops.os_arch);

    /* file system properties */
    PUTPROP(props, "file.separator", sprops.file_separator);
    PUTPROP(props, "path.separator", sprops.path_separator);
    PUTPROP(props, "line.separator", sprops.line_separator);

    /*
     *  user.language
     *  user.region (if user's environmant specifies this)
     *  file.encoding
     *  file.encoding.pkg
     */
    PUTPROP(props, "user.language", sprops.language);
    PUTPROP(props, "file.encoding", sprops.encoding);
#ifdef JAVASE
    PUTPROP(props, "sun.jnu.encoding", sprops.sun_jnu_encoding);
#endif
    if (sprops.region) {
        PUTPROP(props, "user.region", sprops.region);
    }
    PUTPROP(props, "file.encoding.pkg", "sun.io");
    /* unicode_encoding specifies the default endianness */
    PUTPROP(props, "sun.io.unicode.encoding", sprops.unicode_encoding);
    PUTPROP(props, "sun.cpu.isalist", 
	    (sprops.cpu_isalist ? sprops.cpu_isalist : ""));
    PUTPROP(props, "sun.cpu.endian",  sprops.cpu_endian);

    /* !!! DO NOT call PUTPROP_ForPlatformCString before this line !!!
     * !!! I18n properties have not been set up yet !!!
     */

    /* Printing properties */
    PUTPROP(props, "java.awt.printerjob", sprops.printerJob);

    /* Java2D properties */
    PUTPROP(props, "java.awt.graphicsenv", sprops.graphics_env);
#ifdef JAVASE
    PUTPROP_ForPlatformCString(props, "sun.java2d.fontpath", sprops.font_dir);
    /* Preferences properties */
    PUTPROP(props, "java.util.prefs.PreferencesFactory", sprops.util_prefs_PreferencesFactory);

    /* data model */
    if (sizeof(char *) == 4) {
        sprops.data_model = "32";
    } else if (sizeof(char *) == 8) {
        sprops.data_model = "64";
    } else {
        sprops.data_model = "unknown";
    }
    PUTPROP(props, "sun.arch.data.model", sprops.data_model);

    /* patch level */
    PUTPROP(props, "sun.os.patch.level", sprops.patch_level);

    if (sprops.country) {
        PUTPROP(props, "user.country", sprops.country);
    }
    if (sprops.variant) {
        PUTPROP(props, "user.variant", sprops.variant);
    }

#else
    PUTPROP_ForPlatformCString(props, "java.awt.fonts", sprops.font_dir);
#endif

    PUTPROP_ForPlatformCString(props, "java.io.tmpdir", sprops.tmp_dir);

    PUTPROP_ForPlatformCString(props, "user.name", sprops.user_name);
    PUTPROP_ForPlatformCString(props, "user.home", sprops.user_home);

    PUTPROP(props, "user.timezone", sprops.timezone);

    PUTPROP_ForPlatformCString(props, "user.dir", sprops.user_dir);  

    /* CLDC properties */
    PUTPROP(props, "microedition.configuration", "CLDC-1.1");
    PUTPROP(props, "microedition.platform", "j2me");
    PUTPROP(props, "microedition.encoding", "ISO-8859-1");
    PUTPROP(props, "microedition.profiles", "");
    /*
     * Using MIDP-compliant format for microedition.locale value
     * in order to pass JSR-238 TCK.
     */
    PUTPROP(props, "microedition.locale", "en-US");
#ifdef CVM_PROP_MIDP_IMPL
    PUTPROP(props, "com.sun.midp.implementation", CVM_PROP_MIDP_IMPL);
#endif

    /* Generic Connection Framework (GCF) CommConnection property */
    PUTPROP(props, "microedition.commports", sprops.commports);

    result = JVM_InitProperties(env, props, &sprops);
 
    CVMreleaseJavaProperties(&sprops);

    return result;
}

JNIEXPORT jobject JNICALL
Java_java_lang_System_initCldcMidpProperties(JNIEnv *env, jclass cla,
                                         jobject midpProps)
{
#ifndef CVM_DUAL_STACK
    return NULL;
#else
    jmethodID putID = (*env)->GetMethodID(env,
             (*env)->GetObjectClass(env, midpProps),
             "setProperty",
             "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/Object;");

    if (putID != NULL) {
        PUTPROP(midpProps, "microedition.configuration", "CLDC-1.1");
        PUTPROP(midpProps, "microedition.platform", "j2me");
        PUTPROP(midpProps, "microedition.encoding", "ISO-8859-1");
        PUTPROP(midpProps, "microedition.profiles", "MIDP-2.1");
        PUTPROP(midpProps, "microedition.locale", "en-US");
    }
    return midpProps;
#endif
}

/*
 * The following three functions implement setter methods for
 * java.lang.System.{in, out, err}. They are natively implemented
 * because they violate the semantics of the language (i.e. set final
 * variable).
 */
JNIEXPORT void JNICALL
Java_java_lang_System_setIn0(JNIEnv *env, jclass cla, jobject stream)
{
    jfieldID fid =
        (*env)->GetStaticFieldID(env,cla,"in","Ljava/io/InputStream;");
    if (fid == 0)
        return;
    (*env)->SetStaticObjectField(env,cla,fid,stream);
}

JNIEXPORT void JNICALL
Java_java_lang_System_setOut0(JNIEnv *env, jclass cla, jobject stream)
{
    jfieldID fid =
        (*env)->GetStaticFieldID(env,cla,"out","Ljava/io/PrintStream;");
    if (fid == 0)
        return;
    (*env)->SetStaticObjectField(env,cla,fid,stream);
}

JNIEXPORT void JNICALL
Java_java_lang_System_setErr0(JNIEnv *env, jclass cla, jobject stream)
{
    jfieldID fid =
        (*env)->GetStaticFieldID(env,cla,"err","Ljava/io/PrintStream;");
    if (fid == 0)
        return;
    (*env)->SetStaticObjectField(env,cla,fid,stream);
}

JNIEXPORT jclass JNICALL
Java_java_lang_System_getCallerClass(JNIEnv *env, jclass this)
{
    return JVM_GetCallerClass(env, 2);
}

static void cpchars(jchar *dst, char *src, int n)
{
    int i;
    for (i = 0; i < n; i++) {
        dst[i] = src[i];
    }
}

JNIEXPORT jstring JNICALL
Java_java_lang_System_mapLibraryName(JNIEnv *env, jclass ign, jstring libname)
{
    int len;
    int prefix_len = (int)strlen(JNI_LIB_PREFIX);
    int suffix_len = (int)strlen(JNI_LIB_SUFFIX);

    jchar chars[256];
    if (libname == NULL) {
        JNU_ThrowNullPointerException(env, 0);
        return NULL;
    }
    len = (*env)->GetStringLength(env, libname);
    if (len > 240) {
        JNU_ThrowIllegalArgumentException(env, "name too long");
	return NULL;
    }
    cpchars(chars, JNI_LIB_PREFIX, prefix_len);
    (*env)->GetStringRegion(env, libname, 0, len, chars + prefix_len);
    len += prefix_len;
#ifdef CVM_DEBUG
    chars[len++] = '_';
    chars[len++] = 'g';
#endif
    cpchars(chars + len, JNI_LIB_SUFFIX, suffix_len);
    len += suffix_len;

    return (*env)->NewString(env, chars, len);
}


/* 
 * A useful method if you need to do a println before the VM is
 * fully initialized in System.out is available. Uncomment this code
 * and add the prototype to System.java if you want to use it.
 */

#if 0
JNIEXPORT void JNICALL
Java_java_lang_System_println(JNIEnv *env, jclass ign, jstring output)
{
    int len;
    const char* chars;
    jboolean isCopy;
    len = (*env)->GetStringLength(env, output);
    chars = (*env)->GetStringUTFChars(env, output, &isCopy);
    CVMconsolePrintf("%s\n", chars);
    if (isCopy) {
	CVMjniReleaseStringUTFChars(env, output, chars);
    }
}
#endif
